import base64
import errno
import hashlib
import json
import os

import aiohttp
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from middlewared.schema import accepts, Bool, Dict, List, returns, Str, Int
from middlewared.service import CallError, Service

from .items_util import get_item_details
from ..periodic_task import get_local_ip
from middlewared.service import job, private, CallError, Service
import shutil


def aes_encrypt(key, iv, plaintext):
    # 使用PKCS7填充
    padder = padding.PKCS7(128).padder()
    padded_data = padder.update(plaintext) + padder.finalize()

    # 创建AES加密器
    cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
    encryptor = cipher.encryptor()

    # 加密数据
    ciphertext = encryptor.update(padded_data) + encryptor.finalize()
    return ciphertext


def base64_encode(input_bytes):
    encoded_bytes = base64.b64encode(input_bytes)
    return encoded_bytes.decode('utf-8')


def get_AES_Base64(param):
    key = b'12A#GH)90AfjdF&s'
    iv = b'947owi9@PI49#e^h'  # 指定16字节的IV

    # 需要加密的数据
    data = bytes(param, encoding="utf-8")
    result1 = aes_encrypt(key, iv, data)
    print(result1)
    result2 = base64_encode(result1)
    print(result2)
    return result2


# 允许上传的文件类型
ALLOWED_EXTENSIONS = {'png', 'jpg', 'jpeg', 'gif'}
UPLOAD_FOLDER = ""


def allowed_file(filename):
    return '.' in filename and filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS


def base64_to_image(base64_str, output_path):
    # 将 base64 编码解码成图片内容
    if base64_str:
        img_data = base64.b64decode(base64_str)
        # 将图片内容写入到文件中
        with open("/usr/share/truenas/webui/assets/images/desktopscreensaver/" + output_path, "wb") as img_file:
            img_file.write(img_data)


def calculate_hash(s, algorithm="sha256"):
    hash_obj = hashlib.new(algorithm)
    hash_obj.update(s.encode())
    return hash_obj.hexdigest()


class CatalogService(Service):
    class Config:
        cli_namespace = 'app.catalog'

    webservicename = ""

    @accepts(
        Str('item_name'),
        Dict(
            'item_version_details',
            Bool('cache'),  # TODO: Remove this once UI adapts
            Str('catalog', required=True),
            Str('train', required=True),
        )
    )
    @returns(Dict(
        'item_details',
        Str('name', required=True),
        List('categories', items=[Str('category')], required=True),
        Str('app_readme', null=True, required=True),
        Str('location', required=True),
        Bool('healthy', required=True),
        Str('healthy_error', required=True, null=True),
        Str('healthy_error', required=True, null=True),
        Dict('versions', required=True, additional_attrs=True),
        Str('latest_version', required=True, null=True),
        Str('latest_app_version', required=True, null=True),
        Str('latest_human_version', required=True, null=True),
        Str('icon_url', required=True, null=True),
    ))
    def get_item_details(self, item_name, options):
        """
        Retrieve information of `item_name` `item_version_details.catalog` catalog item.
        """
        catalog = self.middleware.call_sync('catalog.get_instance', options['catalog'])
        item_location = os.path.join(catalog['location'], options['train'], item_name)
        if not os.path.exists(item_location):
            raise CallError(f'Unable to locate {item_name!r} at {item_location!r}', errno=errno.ENOENT)
        elif not os.path.isdir(item_location):
            raise CallError(f'{item_location!r} must be a directory')

        questions_context = self.middleware.call_sync('catalog.get_normalised_questions_context')
        return get_item_details(item_location, questions_context, {'retrieve_versions': True})

    @accepts(
        Str('item_name'),
        Dict(
            'item_version_details',
            Int('is_desktop'),
            Str('appid'),
            Str('name'),
        )
    )
    async def create_services_apps(self, item_name, options):
        """
        Retrieve information of `item_name` `item_version_details.catalog` catalog item.
        """
        # print("--------------------------------create_services_apps--------------------------------------------")
        # INSERT INTO services_apps (id, name, is_desktop, app_info, install_time, appid) VALUES(6, 'yichat', 1, '{''key'':''value''}', '20230913', 9567);
        # row_data = {"id": 1231239567, "name": "nebulaoffice", "appid": 9567, "is_desktop": True, "app_info": "",
        #             "install_time": "20230915"}
        app_name = options["name"]
        # 如果查询到数据库该应用存在的话就更新，不存在就插入
        one_app = await self.middleware.call('datastore.query', 'services.apps', [['name', '=', app_name]],
                                             {'prefix': 'scrub_'})
        # print("one_app=", one_app)
        sortnumber_dict = await self.middleware.call('catalog.get_apps_sortnumber_dict')
        sort_number = len(sortnumber_dict) + 1
        try:
            if len(one_app) > 0:
                result = await self.middleware.call('datastore.update', 'services.apps', [['name', '=', app_name]],
                                                    {'is_desktop': 1, 'sort_number': sort_number}, {'prefix': 'scrub_'})
            else:
                options["sort_number"] = sort_number
                result = await self.middleware.call('datastore.insert', 'services.apps', options, {'prefix': 'scrub_'})
        except Exception as e:
            print(e)
            return {"result": "error"}

        # print("result=", result)
        # print("options=", options)
        return {"result": "ok"}

    @accepts(
        Str('app_name'),
        Dict(
            'app_info',
            Int('id')
        )
    )
    async def update_services_apps(self, app_name, dict):
        """
        Retrieve information of `item_name` `item_version_details.catalog` catalog item.
        """
        # print("--------------------------------update_services_apps--------------------------------------------")
        # print("app_name=", app_name)
        try:
            result = await self.middleware.call('datastore.update', 'services.apps', [['name', '=', app_name]],
                                                {'is_desktop': 0}, {'prefix': 'scrub_'})
        except Exception as e:
            print(e)
            return {"result": "error"}
        # print("result=", result)

        return {"result": "ok"}

    @accepts(
        Str('app_name'),
        Dict(
            'app_info',
            Int('id')
        )
    )
    async def delete_services_apps(self, app_name, dict):
        """
        Retrieve information of `item_name` `item_version_details.catalog` catalog item.
        """
        print("--------------------------------delete_services_apps--------------------------------------------")
        # print("dict=", dict)
        try:
            result = await self.middleware.call('datastore.delete', 'services.apps', [['name', '=', app_name]],
                                                {'prefix': 'scrub_'})
        except Exception as e:
            print(e)
            return {"result": "error"}
        # print("result=", result)

        return {"result": "ok"}

    @accepts()
    async def get_mydesktop_apps(self):  # 查询我添加到桌面的应用列表接口
        """
        Retrieve information of `item_name` `item_version_details.catalog` catalog item.
        """
        print("--------------------------------get_mydesktop_apps--------------------------------------------")
        mydesktopapps = []
        default_apps = []
        app_sort_number_dict = {}
        try:
            installed_apps = await self.middleware.call('chart.release.query')
            default_apps = await self.middleware.call('datastore.query', 'services.apps',
                                                      [['is_defaultapp', '=', 1], ['is_defaultapp', '=', 1]],
                                                      {'prefix': 'scrub_'})
            # print("installed_apps=", installed_apps)
            app_sort_number_dict = await self.get_apps_sortnumber_dict()
            for app in installed_apps:
                # print("is_desktop=", app['is_desktop'])
                # print("app=", app)
                is_desktop = app['is_desktop']
                if is_desktop == 1:
                    mydesktopapps.append(app)
        except Exception as e:
            print(e)
        # print("mydesktopapps=", mydesktopapps)
        print("app_sort_number_dict=", app_sort_number_dict)
        sorted_dict = dict(sorted(app_sort_number_dict.items(), key=lambda item: item[1]))
        print("sorted_dict=", sorted_dict)
        return {"installed_apps": mydesktopapps, "default_apps": default_apps,
                "app_sort_number_list": list(sorted_dict.keys())}

    @accepts(
        List('app_name'),
        Dict(
            'app_info',
            Int('id')
        )
    )
    async def update_apps_sort_number(self, mylist, dict):
        # current_datetime = datetime.now()
        # 格式化日期时间字符串
        # formatted_datetime = current_datetime.strftime('%Y-%m-%d %H:%M:%S')
        # print(formatted_datetime)
        # print("-----123----app_name=", mylist)
        final_result = True
        for index, app_name in enumerate(mylist):
            print(app_name, "-for-", index)
            try:
                result = await self.middleware.call('datastore.update', 'services.apps', [['name', '=', str(app_name)]],
                                                    {'sort_number': str(index + 1)}, {'prefix': 'scrub_'})
            except Exception as e:
                result = False
                print(e)
                return {"result": "error"}
        return final_result

    @accepts()
    async def get_apps_sortnumber_dict(self):  # 查询我添加到桌面的应用列表接口
        """
        Retrieve information of `item_name` `item_version_details.catalog` catalog item.
        """
        # print("--------------------------------get_apps_sortnumber_dict--------------------------------------------")
        sortnumber_dict = {}
        try:
            allapps_ondesktop = await self.middleware.call('datastore.query', 'services.apps',
                                                           [['sort_number', '>=', 0]],
                                                           {'prefix': 'scrub_'})
            # print("allapps_ondesktop=", allapps_ondesktop)
            print("len()-allapps_ondesktop=", len(allapps_ondesktop))
            for index, app in enumerate(allapps_ondesktop):
                app_name = app['name']
                sortnumber_dict[str(app_name)] = app['sort_number']
        except Exception as e:
            print(e)
        print("sortnumber_dict=", sortnumber_dict)
        return sortnumber_dict

    @accepts(Str('jsondata'))
    async def hccnas_push(self, jsondata):  # 作为一个公用方法，接收hccnas重启，启动时以及其中安装卸载应用时像应用市场推送信息
        try:
            self.logger.info("hccnas_push----jsondata= %s", jsondata)

            final_result = None
            keyvalue_json = await self.middleware.call("keyvalue.get", "HCCMKT_PUSH_URL", "")
            if keyvalue_json is None or "data" not in keyvalue_json:
                return final_result

            mkt_push_url = keyvalue_json["data"]
            if mkt_push_url is None:
                return final_result

            # 同步推送示例数据:jsondata = {"type": "reboot", "data":{"uuid": "032a467b-c6d0-4de2-9bd9-57817030e9cc", "ip": "127.0.0.1", "port": "8080", "app_id": "1698197767720", "app_name": "hccphoto"}}
            uuid = await self.middleware.call('system.uuid')
            jsonobj = json.loads(jsondata.replace("'", "\""))
            # print("jsonobj=", jsonobj)
            # print("type-jsonobj=", type(jsonobj))
            data = jsonobj['data']
            data['ip'] = get_local_ip()
            data['nasId'] = str(uuid).replace("\n", "")
            data['appId'] = data['app_id']
            # 删除旧有多余key
            if 'data' in jsonobj and 'app_id' in jsonobj['data']:
                del jsonobj['data']['app_id']
            # 入参示例 {"data": {"appId": "1694223494172", "appUrl": "http://132.126.2.11:8080/ui/apps","nasId": "4eb6d2f0-1394-487b-9e6a-3fdf675184b3"}, "type": "start"}
            self.logger.info("hccnas_push----jsonobj= %s", jsonobj)

            # 判断如果type=reboot 增加appUrl={ip}
            if "reboot" == jsonobj['type']:
                data['appUrl'] = get_local_ip()

            jsonobjstr = json.dumps(jsonobj)
            # print("jsonobjstr=", jsonobjstr)
            encodeData = {"encodeData": get_AES_Base64(jsonobjstr)}
            # print("encodeData=", encodeData)
            # print("mkt_push_url=", mkt_push_url)
            async with aiohttp.ClientSession() as session:
                async with session.post(mkt_push_url, json=encodeData) as response:
                    # 处理响应
                    response_text = await response.text()
                    # print("response_text=", response_text)
                    self.logger.info("hccnas_push----response= %s", response)
                    final_result = response
        except Exception as e:
            self.logger.error('hccnas push error: %s', e)
        return final_result

    # 查询网站服务名称 insert into system_keyvalue(id,key,value) values (NULL,"webservicename","家庭超级服务器");
    @accepts()
    async def get_webservicename(self):
        if self.webservicename == "":
            keyvalue_json = await self.middleware.call("keyvalue.get", "webservicename", "")
            print("keyvalue_json=", keyvalue_json)
            self.webservicename = keyvalue_json["webservicename"]
        return {"result": "ok", "webservicename": self.webservicename}

    # 修改网站服务名称
    @accepts(Str('webservicename'))
    async def update_webservicename(self, webservicename):
        result = await self.middleware.call("keyvalue.set", "webservicename", self.webservicename)
        self.webservicename = webservicename
        print("self.webservicename=", self.webservicename)
        print("result=", result)
        return {"result": "ok", "webservicename": self.webservicename}

    # {"pictures": [ {"id":"123", "selected":"1", "pictureurl":"https://img0.baidu.com/it/u=2121279261,
    # 2513596179&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800"}, {"456", "0", "https://img0.baidu.com/it/u=2121279261,
    # 2513596179&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800"}, {"789", "0", "https://img0.baidu.com/it/u=2121279261,
    # 2513596179&fm=253&fmt=auto&app=120&f=JPEG?w=1280&h=800"}]}

    # # 上传桌面屏保图片
    # @accepts(
    #     Str('picturename'),
    #     Str('picture64str')
    # )
    # async def upload_desktopscreensaver(self, picturename, picture64str):
    #     print("--------------------upload_desktopscreensaver------------------------------")
    #     print("picturename", picturename)
    #     print("picture64str", picture64str)
    #     # http://132.126.2.11/ui/assets/images/cloud-logo-f.png
    #     # /usr/share/truenas/webui/assets/images
    #     # insert into system_keyvalue(id,key,value) values (NULL,"desktopscreensaver",'{"pictures": {"f3ccdd27d2000e3f9255a7e3e2c48800": "1.jpg","156005c5baf40ff51a327f1c34f2975b": "2.jpg","799bad5a3b514f096e69bbc4a7896cd9": "3.jpg"},"selectedpicturename": "1.jpg"}');
    #     picturename_hashcode = calculate_hash(picturename)
    #     # 查数据库判断图片名称是否已经存在
    #     keyvalue_json = await self.middleware.call("keyvalue.get", "desktopscreensaver", "")
    #     print("keyvalue_json=", keyvalue_json)
    #     # 如果不存在就写入文件
    #     # 检查当前字典中是否存在目标键值对
    #     if picturename_hashcode not in keyvalue_json["pictures"]:
    #         print(f"数据中不存在 {picturename_hashcode}")
    #         # base64 字符串转换为图片并保存到磁盘上
    #         base64_to_image(picture64str, picturename)
    #         # 更新数据库记录
    #         keyvalue_json["pictures"][picturename_hashcode] = picturename
    #         print("keyvalue_json_new=", keyvalue_json)
    #         result = await self.middleware.call("keyvalue.set", "desktopscreensaver", keyvalue_json)
    #         print("result", result)
    #     else:
    #         print(f"数据中存在 {picturename_hashcode}")
    #     return {"result": "ok", "picturename": picturename_hashcode}

    @accepts(Dict(
        'uploadimg',
        Bool('resume', default=False),
        Str('filename', null=True, default=None),
    ))
    @job(lock='uploadimg')
    async def uploadimg(self, job, options):
        """
        Updates the system using the uploaded .tar file.

        `resume` should be set to `true` if a previous call to this method returned a `CallError` with `errno=EAGAIN`
        meaning that an upgrade can be performed with a warning and that warning is accepted. In that case, re-uploading
        the file is not necessary.

        Use null `destination` to create a temporary location.
        """

        job.check_pipe('input')
        job.set_progress(10, 'Writing uploaded file to disk')
        filename = options["filename"]
        with open("/usr/share/truenas/webui/assets/images/desktopscreensaver/" + filename, "wb") as f:
            await self.middleware.run_in_thread(
                shutil.copyfileobj, job.pipes.input.r, f, 1048576,
            )

        job.set_progress(100, 'upload completed')